package org.hiberlyzer.impl;

import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.hiberlyzer.DataSet;
import org.hiberlyzer.DataSource;
import org.hiberlyzer.MetadataSource;
import org.hiberlyzer.QueryException;
import org.hiberlyzer.QueryLanguage;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.engine.query.QueryPlanCache;
import org.hibernate.hql.QueryTranslator;
import org.hibernate.impl.SessionFactoryImpl;

public class DataSourceImpl implements DataSource
{
	private static final Logger _LOG = Logger.getLogger(DataSourceImpl.class);
	private Session _session;
	private MetadataSource _metadataSource;
	
	public DataSourceImpl(Session session)
	{
		this._session = session;
		this._metadataSource = new MetadataSourceImpl(session);
	}

	public DataSet query(String queryString, QueryLanguage queryLanguage) throws QueryException
	{
		clearSessionBeforeQuery();
		_LOG.debug("Executing " + queryLanguage + " query: " + queryString);
		if(QueryLanguage.HQL.equals(queryLanguage))
		{
			return performHQL(queryString);
		}
		else
		{
			return performSQL(queryString);
		}
	}

	public String translateHql(String queryString)
	{
		String hql = queryString;
		StringBuffer translatedSql = new StringBuffer();
		QueryPlanCache qpc = ((SessionFactoryImpl) _session.getSessionFactory()).getQueryPlanCache();
		QueryTranslator[] qts = qpc.getHQLQueryPlan(hql, false, new HashMap()).getTranslators();
		for (int i = 0; i < qts.length; i++)
		{
			translatedSql.append(qts[i].getSQLString());
		}
		return translatedSql.toString();
	}

	public String getDescription()
	{
		try
		{
			DatabaseMetaData dbMetadata = _session.connection().getMetaData();
			return dbMetadata.getURL() + " (" + dbMetadata.getUserName() + ")";
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return null;
		}
		
	}
	
	public Map getAllClassMetadata()
	{
		return _session.getSessionFactory().getAllClassMetadata();
	}

	private DataSet performSQL(String queryString) throws QueryException
	{
		try
		{
			ResultSet rs = 
				_session.connection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
						ResultSet.CONCUR_READ_ONLY).executeQuery(queryString);
			return new JdbcDataSet(rs);
		} 
		catch (Exception e)
		{
			throw new QueryException("Failed to execute SQL query", e);
		} 
	}

	private DataSet performHQL(String queryString) throws QueryException
	{
		try
		{
			Query query = _session.createQuery(queryString);
			return createHqlDataSet(query);
		} 
		catch (Exception e)
		{
			throw new QueryException("Failed to execute HQL query", e);
		}
	}

	private DataSet createHqlDataSet(Query query)
	{
		// entity datasets will be made if the only thing returned by the query
		// are single entity objects (as opposed to non-entities, or Object[])
		List results = query.list();
		if(results.size() > 0)
		{
			Object firstResult = results.get(0);
			if(null != _session.getSessionFactory().getClassMetadata(firstResult.getClass()))
			{
				return new EntityDataSet(results, _session);
			}
			else
			{
				return new HybridDataSet(results, _session);
			}
		}
		else
		{
			return new EntityDataSet(results, _session);
		}
	}


	private void clearSessionBeforeQuery()
	{
		_LOG.debug("Clearing session");
		this._session.clear();
	}

	public MetadataSource getMetadataSource()
	{
		return this._metadataSource;
	}
}
